class DeepClone {
    constructor() {}

    clone(obj, hash = new WeakMap()) {
        // 如果对象是一个基础类型，那直接返回即可，因为基础类型不存在深拷贝浅拷贝
        if (typeof obj !== 'object' || obj == null) {
            return obj;
        }

        if (obj instanceof Date) return new Date(obj);
        if (obj instanceof RegExp) return new RegExp(obj);

        if (hash.has(obj)) return hash.get(obj);

        // let cloneObj = Array.isArray(obj) ? [] : {};

        let cloneObj = new obj.constructor();
        hash.set(obj, obj);

        Object.keys(obj).forEach((key) => {
            const valueOfKey = obj[key];
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
                if (typeof valueOfKey != 'object' || valueOfKey == null) {
                    cloneObj[key] = valueOfKey;
                } else if (Array.isArray(valueOfKey)) {
                    cloneObj[key] = [];
                    valueOfKey.forEach((val) => {
                        cloneObj[key].push(this.clone(val));
                    });
                } else {
                    cloneObj[key] = this.clone(valueOfKey, hash);
                }
            }
        });

        return cloneObj;
    }
}

// 深拷贝测试

/**
 * 以下测试以数组元素为主
 * 1. 先测试使用concat的浅拷贝
 * 2. 再测试使用了上方DeepClone的深拷贝
 */

// concat 为浅拷贝
let arr1 = [
    { foo: 'bar' },
    { name: 'test' },
    [{ name: '123' }],
    () => {
        console.log('world');
    },
];
let arr2 = [].concat(arr1); // arr2 copy arr1

// 将arr2的第一，第二个元素修改成以下形式，观察arr1的变化
// arr2[0] = 99;
// arr2[1].name = 'zdd';

// console.log(JSON.stringify(arr1)); // arr1:  [{"foo":"bar"},{"name":"zdd"},3]
// 浅拷贝小结：arr1[0]的值没有发生变化，但是arr[1]的值改变了，因为 {name:'zdd'}的引用地址并没有变化

// 引入深拷贝
let deepClone = new DeepClone();

// arr2为arr1的深拷贝
arr2 = deepClone.clone(arr1);

arr2[0] = 99;
arr2[1].name = 'zdd';
arr2[2][0].name = '456';

console.log(JSON.stringify(arr1)); // [{"foo":"bar"},{"name":"test"},[{"name":"123"}],null]
console.log(JSON.stringify(arr2)); // [99,{"name":"zdd"},[{"name":"456"}],null]

// 深拷贝小结：可以看到，两个arr的元素都不相等，即现在已经不存在两个方法的链接
